home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Tool Chest / Text / WASTE / WASTE 1.2a2 / WEInlineInput.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-10-12  |  15.2 KB  |  645 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WEInlineInput.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Inline Input Support
  6.  *
  7.  *  Copyright (c) 1993-1995 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. #define myTypeQDPoint 'QDpt'
  18.  
  19. // static variables
  20.  
  21. static AEEventHandlerUPP _weUpdateActiveInputAreaHandler = NULL;
  22. static AEEventHandlerUPP _wePositionToOffsetHandler = NULL;
  23. static AEEventHandlerUPP _weOffsetToPositionHandler = NULL;
  24.  
  25. pascal OSErr _WEHiliteRangeArray(TextRangeArrayHandle hTray, WEHandle hWE)
  26. {
  27.     WEPtr pWE = *hWE;    // assume WE record is already locked
  28.     TextRangePtr pRange;
  29.     long rangeStart, rangeEnd;
  30.     short hiliteStyle;
  31.     WETextStyle ts;
  32.     short rangeIndex;
  33.     Boolean saveTrayLock;
  34.     OSErr err;
  35.  
  36.     // lock down the range array
  37.     saveTrayLock = _WESetHandleLock((Handle)hTray, true);
  38.     pRange = (*hTray)->fRange;
  39.  
  40.     // walk the hilite range array
  41.     for (rangeIndex = (*hTray)->fNumOfRanges - 1; rangeIndex >= 0; rangeIndex-- )
  42.     {
  43.  
  44.         // the offsets in the range array are relative to the beginning
  45.         // of the active input area: convert them to absolute offsets
  46.         rangeStart = pWE->tsmAreaStart + pRange->fStart;
  47.         rangeEnd = pWE->tsmAreaStart + pRange->fEnd;
  48.         hiliteStyle = pRange->fHiliteStyle;
  49.  
  50.         // take the absolute value of hiliteStyle
  51.         hiliteStyle = ABS(hiliteStyle);
  52.  
  53.         // if hiliteStyle is kCaretPosition, set the selection range
  54.         if (hiliteStyle == kCaretPosition)
  55.         {
  56.             pWE->selStart = rangeStart;
  57.             pWE->selEnd = rangeEnd;
  58.         }
  59.         else
  60.         {
  61.             hiliteStyle -= kRawText;
  62.             // otherwise set the WETextStyle flags of the specified range appropriately
  63.             if ((hiliteStyle >= 0) && (hiliteStyle <= 3))
  64.             {
  65.                 ts.tsFlags = 0x10 + (hiliteStyle << tsTSMSelected);
  66.                 if ((err = _WESetStyleRange(rangeStart, rangeEnd, weDoFlags, &ts, hWE)) != noErr)
  67.                 {
  68.                     goto cleanup;
  69.                 }
  70.             }
  71.         }        
  72.         // go to next text range element
  73.         pRange++;
  74.     }
  75.  
  76.     // clear result code
  77.     err = noErr;
  78.  
  79. cleanup:
  80.     // unlock the range array
  81.     _WESetHandleLock((Handle)hTray, saveTrayLock);
  82.  
  83.     return err;
  84. }
  85.  
  86. pascal OSErr _WEHandleUpdateActiveInputArea(AppleEvent *ae, AppleEvent *reply,
  87.         long handlerRefCon)
  88. {
  89. #pragma unused(reply, handlerRefCon)
  90.     WEHandle hWE;
  91.     WEPtr pWE;
  92.     AEDesc text;
  93.     AEDesc hiliteTray;
  94.     TextRange pinRange;
  95.     long totalLength;
  96.     long fixLength;
  97.     long tsmOffset;
  98.     DescType returnedType;
  99.     long actualSize;
  100.     GrafPtr savePort;
  101.     WEActionHandle hAction;
  102.     Boolean saveAutoScroll;
  103.     Boolean saveTextLock;
  104.     Boolean saveWELock;
  105.     OSErr err;
  106.  
  107.     hWE = NULL;
  108.  
  109.     // initialize descriptors to null values
  110.     text.descriptorType = typeNull;
  111.     text.dataHandle = NULL;
  112.     hiliteTray.descriptorType = typeNull;
  113.     hiliteTray.dataHandle = NULL;
  114.  
  115.     // extract WE handle
  116.     err = AEGetParamPtr(ae, keyAETSMDocumentRefcon, typeLongInteger, &returnedType,
  117.         (Ptr)&hWE, sizeof(hWE), &actualSize);
  118.     if (err != noErr)
  119.     {
  120.         goto cleanup;
  121.     }
  122.  
  123.     // lock the WE handle
  124.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  125.     pWE = *hWE;
  126.  
  127.     // return an error code if this instance is read-only
  128.     err = weReadOnlyErr;
  129.     if (BTST(pWE->features, weFReadOnly))
  130.         goto cleanup;
  131.  
  132.     // call the pre-update callback, if present
  133.     if (pWE->tsmPreUpdate != NULL)
  134.     {
  135.         CallWETSMPreUpdateProc(hWE, pWE->tsmPreUpdate);
  136.     }
  137.  
  138.     // hide the caret if it's showing
  139.     if (BTST(pWE->flags, weFCaretVisible))
  140.     {
  141.         _WEBlinkCaret(hWE);
  142.     }
  143.     
  144.     // extract the text descriptor
  145.     if ((err = AEGetParamDesc(ae, keyAETheData, typeChar, &text)) != noErr)
  146.     {
  147.         goto cleanup;
  148.     }
  149.     
  150.     // get total length of text in the active input area
  151.     totalLength = GetHandleSize(text.dataHandle);
  152.  
  153.     // extract the length of confirmed text in the active input area
  154.     err = AEGetParamPtr(ae, keyAEFixLength, typeLongInteger, &returnedType,
  155.         &fixLength, sizeof(fixLength), &actualSize);
  156.     if (err != noErr)
  157.     {
  158.         goto cleanup;
  159.     }
  160.     // if fixLength = -1, all text is confirmed
  161.     if (fixLength == -1)
  162.     {
  163.         fixLength = totalLength;
  164.     }
  165.     // if there's currently no active input area, open one
  166.     if (pWE->tsmAreaStart == kInvalidOffset)
  167.     {
  168.         pWE->tsmAreaStart = pWE->selStart;
  169.         pWE->tsmAreaEnd = pWE->selEnd;
  170.         
  171.         // are we tracking a typing sequence?
  172.         if (WEIsTyping(hWE) == false)
  173.         {
  174.  
  175.             // nope; so start a new one
  176.             // increment modification count
  177.             pWE->modCount++;
  178.  
  179.             // if undo support is enabled, the inline session just started may initiate
  180.             if (BTST(pWE->features, weFUndoSupport))
  181.             {
  182.                 WEClearUndo(hWE);
  183.                 if (WENewAction(pWE->selStart, pWE->selEnd, 0, weAKTyping, 0, hWE, &hAction) == noErr)
  184.                 {
  185.                     if (WEPushAction(hAction) != noErr)
  186.                     {
  187.                         ;
  188.                     }
  189.                 }
  190.             }
  191.         }
  192.     }
  193.  
  194.     tsmOffset = pWE->tsmAreaStart;
  195.  
  196.     // the new text replaces whatever is in the active input area
  197.     if ((err = _WEDeleteRange(tsmOffset, pWE->tsmAreaEnd, hWE)) != noErr)
  198.     {
  199.         goto cleanup;
  200.     }
  201.  
  202.     // synchronize the null style, so font script matches the keyboard script
  203.     _WESynchNullStyle(hWE);
  204.  
  205.     // set the port font for good measure
  206.     GetPort(&savePort);
  207.     SetPort(pWE->port);
  208.     TextFont(pWE->nullStyle.runStyle.tsFont);
  209.     SetPort(savePort);
  210.  
  211.     // temporarily lock the text
  212.     saveTextLock = _WESetHandleLock(text.dataHandle, true);
  213.  
  214.     // insert the text
  215.     if ((err = _WEInsertText(tsmOffset, *(text.dataHandle), totalLength, hWE)) != noErr)
  216.     {
  217.         goto cleanup;
  218.     }
  219.     // unlock the text
  220.     _WESetHandleLock(text.dataHandle, saveTextLock);
  221.  
  222.     // extract pin range
  223.     err = AEGetParamPtr(ae, keyAEPinRange, typeTextRange, &returnedType, &pinRange,
  224.         sizeof(pinRange), &actualSize);
  225.     if (err == noErr)
  226.     {
  227.         // we want absolute offsets
  228.         pinRange.fStart = pinRange.fStart + tsmOffset;
  229.         pinRange.fEnd = pinRange.fEnd + tsmOffset;
  230.     }
  231.     else
  232.     {
  233.         // a missing pin range descriptor isn't an error; everything else is
  234.         if (err != errAEDescNotFound)
  235.         {
  236.             goto cleanup;
  237.         }
  238.         // default pin range is active input area
  239.         pinRange.fStart = tsmOffset;
  240.         pinRange.fEnd = pWE->tsmAreaEnd;
  241.     }
  242.  
  243.     // NOTE: if fixLength = totalLength, the inline input session is over, so, in theory,
  244.     // hiliteTray should either be missing or not specify any range to be underlined.
  245.     // Unfortunately, some input methods (like Apple's input method for Simplified Chinese)
  246.     // do specify kConvertedText (= thin black underline) for the whole text when
  247.     // the text is confirmed (is this a bug?).  To work around this, we deliberately ignore
  248.     // the hiliteTray parameter when fixLength = totalLength.
  249.  
  250.     if (fixLength != totalLength)
  251.     {
  252.         // extract the highlight range array
  253.         if ((err = AEGetParamDesc(ae, keyAEHiliteRange, typeTextRangeArray, &hiliteTray)) != noErr)
  254.         {
  255.             if (err != errAEDescNotFound)
  256.             {
  257.                 goto cleanup;
  258.             }
  259.         }
  260.     }
  261.  
  262.     if (hiliteTray.dataHandle != NULL)
  263.     {
  264.         if ((err = _WEHiliteRangeArray((TextRangeArrayHandle)hiliteTray.dataHandle, hWE)) != noErr)
  265.         {
  266.             goto cleanup;
  267.         }
  268.     }
  269.     else
  270.     {
  271.         pWE->selStart = tsmOffset + fixLength;
  272.         pWE->selEnd = pWE->selStart;
  273.     }
  274.  
  275.     // temporarily disable auto-scroll, as we need to scroll manually according to pinRange
  276.     saveAutoScroll = BTST(pWE->features, weFAutoScroll) ? true : false;
  277.     BCLR(pWE->features, weFAutoScroll);
  278.     
  279.     // redraw the active input area
  280.     if ((err = _WERedraw(tsmOffset, tsmOffset + totalLength, hWE)) != noErr)
  281.     {
  282.         goto cleanup;
  283.     }
  284.  
  285.     if (saveAutoScroll)
  286.     {
  287.     
  288.         // re-enable auto-scroll
  289.         BSET(pWE->features, weFAutoScroll);
  290.  
  291.         // scroll the pin range into view
  292.         if (_WEScrollIntoView(pinRange.fStart, hWE) == false)
  293.         {
  294.             if (pinRange.fStart != pinRange.fEnd)
  295.             {
  296.                 _WEScrollIntoView(pinRange.fEnd, hWE);
  297.             }
  298.         }
  299.     }
  300.  
  301.     // update the boundaries of the active input area
  302.     // if fixLength = totalLength, the inline input session is over: close the active input area
  303.     if (fixLength == totalLength)
  304.     {
  305.         pWE->tsmAreaStart = kInvalidOffset;
  306.         pWE->tsmAreaEnd = kInvalidOffset;
  307.  
  308.         // adjust undo buffer (if any) for the confirmed text
  309.         _WEAdjustUndoRange(fixLength, hWE);
  310.     }
  311.     else
  312.     {
  313.         // otherwise, fixLength defines the boundaries of the active input area
  314.         pWE->tsmAreaStart = tsmOffset + fixLength;
  315.         pWE->tsmAreaEnd = tsmOffset + totalLength;
  316.     }
  317.  
  318.     // call the post-update callback, if present
  319.     if (pWE->tsmPostUpdate != NULL)
  320.     {
  321.         CallWETSMPostUpdateProc(hWE, fixLength, pWE->tsmAreaStart, pWE->tsmAreaEnd,
  322.             pinRange.fStart, pinRange.fEnd, pWE->tsmPostUpdate);
  323.     }
  324.     // clear result code
  325.     err = noErr;
  326.  
  327. cleanup:
  328.     // clean up
  329.     AEDisposeDesc(&text);
  330.     AEDisposeDesc(&hiliteTray);
  331.  
  332.     // unlock the WE record
  333.     if (hWE !=NULL)
  334.     {
  335.         _WESetHandleLock((Handle) hWE, saveWELock);
  336.     }
  337.  
  338.     // return result code
  339.     return err;
  340. }
  341.  
  342.  
  343. pascal OSErr _WEHandlePositionToOffset (AppleEvent *ae, AppleEvent *reply,
  344.         long handlerRefCon)
  345. {
  346. #pragma unused(handlerRefCon)
  347.     WEHandle hWE;
  348.     WEPtr pWE;
  349.     Point position;
  350.     LongPt thePoint;
  351.     short regionClass;
  352.     long offset;
  353.     DescType returnedType;
  354.     long actualSize;
  355.     GrafPtr savePort;
  356.     char edge;
  357.     Boolean saveWELock;
  358.     OSErr err;
  359.  
  360.     hWE = NULL;
  361.  
  362.     // extract WE handle
  363.     err = AEGetParamPtr(ae, keyAETSMDocumentRefcon, typeLongInteger, &returnedType,
  364.         &hWE, sizeof(hWE), &actualSize);
  365.     if (err != noErr)
  366.     {
  367.         goto cleanup;
  368.     }
  369.  
  370.     // lock the WE record
  371.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  372.     pWE = *hWE;
  373.  
  374.     // extract position parameter
  375.     err = AEGetParamPtr(ae, keyAECurrentPoint, myTypeQDPoint, &returnedType, &position,
  376.         sizeof(position), &actualSize);
  377.     if (err != noErr)
  378.     {
  379.             goto cleanup;
  380.     }
  381.     // convert position to local...
  382.     GetPort(&savePort);
  383.     SetPort(pWE->port);
  384.     GlobalToLocal(&position);
  385.     SetPort(savePort);
  386.  
  387.     // ...and long coordinates
  388.     WEPointToLongPoint(position, &thePoint);
  389.  
  390.     // find the byte offset and the edge value corresponding to the given position
  391.     offset = WEGetOffset(&thePoint, &edge, hWE);
  392.  
  393.     // determine the region class
  394.     if (WELongPointInLongRect(&thePoint, &pWE->viewRect))
  395.     {
  396.         if (_WEOffsetInRange(offset, edge, pWE->tsmAreaStart, pWE->tsmAreaEnd))
  397.         {
  398.               regionClass = kTSMInsideOfActiveInputArea;
  399.             // if the given position is within the active input area, we're supposed to return
  400.             // an offset relative to the beginning of this area (thanks, Martin!)
  401.             offset -= pWE->tsmAreaStart;
  402.         }
  403.         else
  404.         {
  405.             // otherwise the offset is relative to the beginning of the body
  406.             regionClass = kTSMInsideOfBody;
  407.         }
  408.     }
  409.     else
  410.     {
  411.         regionClass = kTSMOutsideOfBody;
  412.     }
  413.     
  414.     // add region class parameter to reply
  415.     err = AEPutParamPtr(reply, keyAERegionClass, typeShortInteger, ®ionClass,
  416.         sizeof(regionClass));
  417.     if (err != noErr)
  418.     {
  419.         goto cleanup;
  420.     }
  421.     
  422.     // add offset parameter to reply
  423.     if ((err = AEPutParamPtr(reply, keyAEOffset, typeLongInteger, &offset, sizeof(offset))) != noErr) 
  424.     {
  425.         goto cleanup;
  426.     }
  427.     
  428.     // add edge parameter to reply
  429.     if ((err = AEPutParamPtr(reply, keyAELeftSide, typeBoolean, &edge, sizeof(edge))) != noErr)
  430.     {
  431.         goto cleanup;
  432.     }
  433.  
  434.     // clear result code
  435.     err = noErr;
  436.  
  437. cleanup:
  438.     // unlock the WE record
  439.     if (hWE != NULL)
  440.     {
  441.         _WESetHandleLock((Handle) hWE, saveWELock);
  442.     }
  443.     
  444.     // return result code
  445.     return err;
  446. }
  447.  
  448. pascal OSErr _WEHandleOffsetToPosition(AppleEvent *ae, AppleEvent *reply, long handlerRefCon)
  449. {
  450. #pragma unused(handlerRefCon)
  451.     WEHandle hWE;
  452.     WEPtr pWE;
  453.     long offset;
  454.     LongPt thePoint;
  455.     Point position;
  456.     short lineHeight;
  457.     DescType returnedType;
  458.     long actualSize;
  459.     GrafPtr savePort;
  460.     Boolean saveWELock;
  461.     OSErr err;
  462.  
  463.     hWE = NULL;
  464.  
  465.     // extract WE handle
  466.     err = AEGetParamPtr(ae, keyAETSMDocumentRefcon, typeLongInteger, &returnedType, &hWE,
  467.         sizeof(hWE), &actualSize);
  468.     if (err != noErr)
  469.     {
  470.         goto cleanup;
  471.     }
  472.  
  473.     // lock the WE record
  474.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  475.     pWE = *hWE;
  476.  
  477.     // if there's no active input area, return errOffsetInvalid
  478.     if (pWE->tsmAreaStart < 0)
  479.     {
  480.         err = errOffsetInvalid;
  481.         goto cleanup;
  482.     }
  483.     
  484.     // extract the offset parameter
  485.     err = AEGetParamPtr(ae, keyAEOffset, typeLongInteger, &returnedType, &offset, 
  486.         sizeof(offset), &actualSize);
  487.     if (err != noErr)
  488.     {
  489.         goto cleanup;
  490.     }
  491.  
  492.     // offset is relative to the beginning of the active input area; we want an absolute offset
  493.     offset += pWE->tsmAreaStart;
  494.  
  495.     // make sure the offset is within the input area
  496.     if ((offset < pWE->tsmAreaStart) || (offset >= pWE->tsmAreaEnd))
  497.     {
  498.         err = errOffsetInvalid;
  499.         goto cleanup;
  500.     }
  501.  
  502.     // find the position corresponding to the given offset (in long coordinates)
  503.     WEGetPoint(offset, &thePoint, &lineHeight, hWE);
  504.     thePoint.v += lineHeight;
  505.  
  506.     // make sure offset is within view rectangle
  507.     if (!WELongPointInLongRect(&thePoint, &pWE->viewRect))
  508.     {
  509.         err = errOffsetIsOutsideOfView;
  510.         goto cleanup;
  511.     }
  512.  
  513.     // convert the point to short...
  514.     WELongPointToPoint(&thePoint, &position);
  515.  
  516.     // ...and global coordinates
  517.     GetPort(&savePort);
  518.     SetPort(pWE->port);
  519.     LocalToGlobal(&position);
  520.     SetPort(savePort);
  521.  
  522.     // add keyAEPoint parameter to the reply Apple event
  523.     if ((err = AEPutParamPtr(reply, keyAEPoint, myTypeQDPoint, &position, sizeof(position))) != noErr)
  524.     {
  525.         goto cleanup;
  526.     }
  527.  
  528.     // add keyAETSMTextFont parameter to the reply Apple event
  529.     err = AEPutParamPtr(reply, keyAETSMTextFont, typeShortInteger, &pWE->nullStyle.runStyle.tsFont,
  530.         sizeof(pWE->nullStyle.runStyle.tsFont));
  531.     if (err != noErr)
  532.     {
  533.         goto cleanup;
  534.     }
  535.     
  536.     // add keyAETSMTextPointSize parameter to the reply Apple event
  537.     err = AEPutParamPtr(reply, keyAETSMTextPointSize, typeShortInteger, 
  538.         &pWE->nullStyle.runStyle.tsSize, sizeof(pWE->nullStyle.runStyle.tsSize));
  539.     if (err != noErr)
  540.     {
  541.         goto cleanup;
  542.     }
  543.  
  544.     // add keyAETextLineAscent parameter to the reply Apple event
  545.     err = AEPutParamPtr(reply, keyAETextLineAscent, typeShortInteger, 
  546.         &pWE->nullStyle.runAscent, sizeof(pWE->nullStyle.runAscent));
  547.     if (err != noErr)
  548.     {
  549.         goto cleanup;
  550.     }
  551.     
  552.     // add keyAETextLineHeight parameter to the reply Apple event
  553.     err = AEPutParamPtr(reply, keyAETextLineHeight, typeShortInteger, 
  554.         &pWE->nullStyle.runHeight, sizeof(pWE->nullStyle.runHeight));
  555.     if (err != noErr)
  556.     {
  557.         goto cleanup;
  558.     }
  559.     
  560.     // clear result code
  561.     err = noErr;
  562.  
  563. cleanup:
  564.     // unlock the WE record
  565.     if (hWE != NULL)
  566.     {
  567.         _WESetHandleLock((Handle) hWE, saveWELock);
  568.     }
  569.     
  570.     // return result code
  571.     return err;
  572. }
  573.  
  574. pascal OSErr WEInstallTSMHandlers(void)
  575. {
  576.     OSErr err;
  577.     
  578.     // the first time we're called, create routine descriptors for our Apple event handlers
  579.     if (_weUpdateActiveInputAreaHandler == NULL)
  580.     {
  581.         _weUpdateActiveInputAreaHandler = NewAEEventHandlerProc(_WEHandleUpdateActiveInputArea);
  582.         _wePositionToOffsetHandler = NewAEEventHandlerProc(_WEHandlePositionToOffset);
  583.         _weOffsetToPositionHandler = NewAEEventHandlerProc(_WEHandleOffsetToPosition);
  584.     }
  585.  
  586.     // install Apple Event handlers to be used by Text Service components
  587.     err = AEInstallEventHandler(kTextServiceClass, kUpdateActiveInputArea,
  588.         _weUpdateActiveInputAreaHandler, 0, false);
  589.     if (err != noErr)
  590.     {
  591.         return err;
  592.     }
  593.         
  594.     err = AEInstallEventHandler(kTextServiceClass, kPos2Offset, 
  595.         _wePositionToOffsetHandler, 0, false);
  596.     if (err != noErr)
  597.     {
  598.         return err;
  599.     }
  600.     
  601.     err = AEInstallEventHandler(kTextServiceClass, kOffset2Pos, 
  602.         _weOffsetToPositionHandler, 0, false);
  603.     if (err != noErr)
  604.     {
  605.         return err;
  606.     }
  607.     
  608.     // return result code
  609.     return noErr;
  610. }
  611.  
  612. pascal OSErr WERemoveTSMHandlers(void)
  613. {
  614.     OSErr err;
  615.     
  616.     // return an error code if WEInstallTSMHandlers has never been called
  617.     if (_weUpdateActiveInputAreaHandler == NULL)
  618.         return errAEHandlerNotFound;
  619.  
  620.     // remove the handlers
  621.     err = AERemoveEventHandler(kTextServiceClass, kUpdateActiveInputArea,
  622.         _weUpdateActiveInputAreaHandler, false);
  623.     if (err != noErr)
  624.     {
  625.         return err;
  626.     }
  627.         
  628.     err = AERemoveEventHandler(kTextServiceClass, kPos2Offset, 
  629.         _wePositionToOffsetHandler, false);
  630.     if (err != noErr)
  631.     {
  632.         return err;
  633.     }
  634.     
  635.     err = AERemoveEventHandler(kTextServiceClass, kOffset2Pos, 
  636.         _weOffsetToPositionHandler, false);
  637.     if (err != noErr)
  638.     {
  639.         return err;
  640.     }
  641.     
  642.     // clear result code
  643.     return noErr;
  644. } // WERemoveTSMHandlers
  645.